package com.dinim.physics2D;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.dinim.object.IObject2D;
import com.dinim.object.Object2D;

public class ContactResolver2 {
    
    private interface ObjectValueReturner{
        public abstract double getValue(IObject2D object);
    }
    private interface ValueComparator{
        public abstract boolean compareValues(double superbjectValue, double subObjectValue);
    }
    
    private Comparator verticalComparator = new Comparator(){
        public int compare(Object o1, Object o2) {
            IObject2D obj1 = (IObject2D)o1;
            IObject2D obj2 = (IObject2D)o2;
            double top1 = obj1.getBoundingBox().getTop();
            double top2 = obj2.getBoundingBox().getTop();
            int value = (int)Math.round(top2 - top1);
            //System.out.println(value);
//            if(value == 0){
//                return 1;
//            }
            return value;
            //return (int)Math.round(top1 - top2);
//            if(top1 > top2){
//                return -1;
//            }else if(top1 < top2){
//                return 1;
//            }else{
//                return 0;
//            }
        }
    };
        
    private Comparator horizontalComparator = new Comparator(){
        public int compare(Object o1, Object o2) {
            IObject2D obj1 = (IObject2D)o1;
            IObject2D obj2 = (IObject2D)o2;
            double top1 = obj1.getBoundingBox().getLeft();
            double top2 = obj2.getBoundingBox().getLeft();
            int value = (int)Math.round(top2 - top1);
            return value;
        }
    };
    
    private ObjectValueReturner getBottom = new ObjectValueReturner(){
        public double getValue(IObject2D object) {
            return object.getBoundingBox().getBottom();
        }
    };
    
    private ObjectValueReturner getTop = new ObjectValueReturner(){
        public double getValue(IObject2D object) {
            return object.getBoundingBox().getTop();
        }
    };
    
    private ObjectValueReturner getRight = new ObjectValueReturner(){
        public double getValue(IObject2D object) {
            return object.getBoundingBox().getRight();
        }
    };
    
    private ObjectValueReturner getLeft = new ObjectValueReturner(){
        public double getValue(IObject2D object) {
            return object.getBoundingBox().getLeft();
        }
    };
    
    private ValueComparator superBiggerThanSub = new ValueComparator(){
        public boolean compareValues(double superObjectValue, double subObjectValue) {
            if(superObjectValue >= subObjectValue){
                return true;
            }else{
                return false;
            }
        }
    };
    
    private ValueComparator superSmallerThanSub = new ValueComparator(){
        public boolean compareValues(double superObjectValue, double subObjectValue) {
            if(superObjectValue <= subObjectValue){
                return true;
            }else{
                return false;
            }
        }
    };

    private List getSortedObjectList(List objects, Comparator comparator){
        List sortedObjects = new ArrayList(objects);
        Collections.sort(sortedObjects, comparator);
        return sortedObjects;
    }

    public List getCollidingObjects(List objects){
        List verticallySortedObjects = getSortedObjectList(objects, verticalComparator);
        List horizontallySortedObjects = getSortedObjectList(objects, horizontalComparator);
        List collidingObjects = new ArrayList();
        for(int i = 0; i < verticallySortedObjects.size(); i++){
            IObject2D superobject = (IObject2D)verticallySortedObjects.get(i);
            List verticallyBorderingList = getBorderingObjects(superobject, verticallySortedObjects, 
                                                               i, +1, 
                                                               getBottom, getTop, 
                                                               superBiggerThanSub);
            if(verticallyBorderingList.size() > 0){
	            List horizontallyBorderingListPos = getBorderingObjects(superobject, horizontallySortedObjects, 
                                                	                    horizontallySortedObjects.indexOf(superobject), +1, 
                                                	                    getRight, getLeft, 
                                                	                    superBiggerThanSub);
	            List horizontallyBorderingListNeg = getBorderingObjects(superobject, horizontallySortedObjects, 
                                                	                    horizontallySortedObjects.indexOf(superobject), -1, 
                                                	                    getLeft, getRight, 
                                                	                    superSmallerThanSub);
                List resolved;
                resolved = resolveHorizontalAndVertical(verticallyBorderingList, horizontallyBorderingListPos);
                for(int r = 0; r < resolved.size(); r++){
                    collidingObjects.add(new Object2D[]{(Object2D)superobject, (Object2D)resolved.get(r)});
                }
                resolved = resolveHorizontalAndVertical(verticallyBorderingList, horizontallyBorderingListNeg);
                for(int r = 0; r < resolved.size(); r++){
                    collidingObjects.add(new Object2D[]{(Object2D)superobject, (Object2D)resolved.get(r)});
                }
            }
            horizontallySortedObjects.remove(superobject);
        }
        return collidingObjects;
    }
    
    private List resolveHorizontalAndVertical(List list1, List list2){
        List resolvedList = new ArrayList();
        List smaller;
        List bigger;
        if(list1.size() < list2.size()){
            smaller = list1;
            bigger = list2;
        }else{
            smaller = list2;
            bigger = list1;
        }
        for(int i = 0; i < smaller.size(); i++){
            if(bigger.contains(smaller.get(i))){
                resolvedList.add(smaller.get(i));
            }
        }
        return resolvedList;
    }
    
    private List getBorderingObjects(IObject2D superobject, List objectlist, 
                                      int index, int direction,
                                      ObjectValueReturner superObjectVR, ObjectValueReturner subObjectVR,
                                      ValueComparator vc){
        List borderingObjects = new ArrayList();
        int j = direction;
        boolean run = true;
        while(run && ((objectlist.size()-1) >= (index+j)) && ((index+j) >= 0)){
            IObject2D nextObject = (IObject2D)objectlist.get(index+j);
            double superObjectValue = Math.round(superObjectVR.getValue(superobject));
            double subObjectValue = Math.round(subObjectVR.getValue(nextObject));
            if(vc.compareValues(superObjectValue, subObjectValue)){
                borderingObjects.add(nextObject);
            } else {
                run = false;
            }
            j = j + direction;
        }
        return borderingObjects;
    }
}
